//
//  NEDRequest+Promise.m
//  NEDNetwork
//
//  Created by 000 on 2021/12/26.
//

#import "NEDRequest+Promise.h"
#import <cocore/co_queue.h>
#import <YYModel/NSObject+YYModel.h>

#pragma mark - retry


typedef BOOL (^NEDPromiseRetryProgressBlock)(NSInteger times, NSError * error);
typedef id __nullable (^NEDPromiseRetryWorkBlock)(void);

@interface COPromise(PriviteRetry)
- (void)observeWithFulfill:(void(^)(id __nullable value))onFulfill reject:(void(^)(NSError *error))onReject;
@end

static void NEDPromiseRetryAttempt(COPromise *promise, NSInteger count,NSTimeInterval interval, NEDPromiseRetryProgressBlock progress, NEDPromiseRetryWorkBlock work) {
    __auto_type retrier = ^(id __nullable value) {
        if ([value isKindOfClass:[NSError class]]) {
            if (count <= 0 || (progress && !progress(count, value))) {
                [promise reject:value];
            } else {
                dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), co_get_current_queue(), ^{
                    NEDPromiseRetryAttempt(promise, count - 1, interval, progress, work);
                });
            }
        } else {
            [promise fulfill:value];
        }
    };
    id value = work();
    if ([value isKindOfClass:[COPromise class]]) {
        COPromise * coP = (COPromise *)value;
        if([coP respondsToSelector:@selector(observeWithFulfill:reject:)]){
            [(COPromise *)value observeWithFulfill:retrier reject:retrier];
        }else{
            NSLog(@"未实现该函数，或者函数有变：%@",NSStringFromSelector(@selector(observeWithFulfill:reject:)));
        }
    } else  {
        retrier(value);
    }
}

@interface COPromise(Retry)
+ (COPromise *)retryTimes:(NSInteger)retryTimes
            delayInterval:(NSTimeInterval)delayInterval
                 progress:(nullable NEDPromiseRetryProgressBlock)progress
                    retry:(NEDPromiseRetryWorkBlock)work;
@end
@implementation COPromise(Retry)

+ (COPromise *)retryTimes:(NSInteger)retryTimes
            delayInterval:(NSTimeInterval)delayInterval
                 progress:(nullable NEDPromiseRetryProgressBlock)progress
                    retry:(NEDPromiseRetryWorkBlock)work{
    NSParameterAssert(work);
    
    COPromise * promise = [COPromise new];
    NEDPromiseRetryAttempt(promise, retryTimes, delayInterval, progress, work);
    return promise;
}

@end




#pragma mark - request


@implementation NEDRequest (Promise)

- (COPromise<id> *)promise{
    __weak typeof(&*self)weakSelf = self;
    COPromise * cp = [COPromise new];
    [self executeWithSuccess:^(NSString * _Nullable message, id  _Nonnull result) {
        [cp fulfill:result];
    } failure:^(NSString * _Nullable failureMsg, NSInteger errorCode, NSError * _Nonnull error) {
        __strong __typeof(&*weakSelf)strongSelf = weakSelf;
        if(strongSelf.config.retryTimes > 0 &&
           [cp respondsToSelector:@selector(observeWithFulfill:reject:)]){
            COPromise * retryPromiss = [COPromise retryTimes:strongSelf.config.retryTimes delayInterval:strongSelf.config.retryDelayInterval progress:^BOOL(NSInteger times, NSError *error) {
                NSLog(@"重试次数：%ld 错误信息：%@",strongSelf.config.retryTimes - times + 1,error);
                return (times >= 0);
            } retry:^id _Nullable(void){
                COPromise * rePromiss = [COPromise new];
                NEDRequest * reRequest = strongSelf.yy_modelCopy;
                [reRequest executeWithSuccess:^(NSString * _Nullable message, id  _Nonnull result) {
                    [rePromiss fulfill:result];
                } failure:^(NSString * _Nullable failureMsg, NSInteger errorCode, NSError * _Nonnull error) {
                    NSError * er = [NSError co_errorWithCode:errorCode userInfo:error.userInfo errorMessage:failureMsg];
                    [rePromiss reject:er];
                }];
                return rePromiss;
            }];
            COPromiseFulfill fulfill = ^(id value){
                [cp fulfill:value];
            };
            COPromiseReject reject = ^(NSError *error){
                NSError * er = [NSError co_errorWithCode:errorCode userInfo:error.userInfo errorMessage:failureMsg];
                [cp reject:er];
            };
            [retryPromiss observeWithFulfill:fulfill reject:reject];
        }else{
            NSError * er = [NSError co_errorWithCode:errorCode userInfo:error.userInfo errorMessage:failureMsg];
            [cp reject:er];
        }
    }];
    
    return cp;
}

-(id)await{
    id result = await(self.promise);
    NSError * error = co_getError();
    if(error){
        //处理错误信息
        NSLog(@"请求失败%@",error);
    }
    return result;
}

@end


@implementation COPromise(CO)

- (id _Nullable)await{
    return await(self);;
}

@end



@implementation NSError(CO)

+ (instancetype)co_errorWithCode:(NSInteger)code
                        userInfo:(nullable NSDictionary<NSErrorUserInfoKey, id> *)dict
                    errorMessage:(NSString *)errorMessage{
    NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithDictionary:dict];
    if(!errorMessage){
        errorMessage = dict[NSDebugDescriptionErrorKey];
    }
    dic[@"coErrorMessage"] = errorMessage;
    
    NSError * er = [NSError errorWithDomain:@"magic box requst" code:code userInfo:dic];
    return er;
}

- (NSString *)co_errorMessageInfo{
    NSDictionary * errorInfo = self.userInfo;
    if(errorInfo &&
       errorInfo[@"coErrorMessage"] &&
       [errorInfo[@"coErrorMessage"] isKindOfClass:NSString.class]){
        return errorInfo[@"coErrorMessage"];
    }
    return nil;
}

@end
